package factory;
import java.util.ArrayList;
import java.util.List;
import constraints_validator.ValidatorSolution;

import value_object.InstanceVO;
import value_object.SolutionVO;
import value_object.data.Acquisition;
import value_object.data.Strip;

/**
 * This factory class is used to create a solution from an instance
 */
public class SolutionFactory {
	private InstanceVO instance;
	private List<Acquisition> acqNotAddedList;
	
	/**
	 * Constructor
	 * @param instance instance object
	 */
	public SolutionFactory(InstanceVO instance) {
		this.instance = instance;
		this.acqNotAddedList =  new ArrayList <Acquisition> ();
	}
	
	public void addAcqNotAdded(Acquisition acq) {
		acqNotAddedList.add(acq);
	}
	
	public void setAcqNotAddedList(List<Acquisition> acqList) {
		this.acqNotAddedList = acqList;
	}
	
	public List<Acquisition> getAcqNotAddedList() {
		return acqNotAddedList;
	}
	
	/**
	 * This method will return a solution, which respects all the 4 constraints, from the instance
	 * <p>
	 * @return SolutionVO - the solution
	 */
	
	public SolutionVO getSolution() {
		SolutionVO solution = new SolutionVO(instance.getData_set_number(), instance.getGrid_number(), instance.getTrack_number());
		
		List<Strip> allStrips = instance.getAllStrips();
		
		boolean firstime = true;
		for (Strip strip : allStrips) {
			
			//set a random direction to strip
			int direction = (int) (Math.random() * 100) % 2;
			strip.setStrip_direction(direction);
			
			//add first strip to the solution
			if (firstime && validateConstraint1(strip)) {
				Acquisition acq = new Acquisition();
				acq.setStrip_index(strip.getStrip_index());
				acq.setAcquisition_direction(strip.getStrip_direction());
				solution.addAcquisition(acq);
				firstime = false;
				
				continue; //go back the loop
			}

			//validate every the next strip
			if (validateConstraint1(strip) && 
				 validateConstraint2(strip, solution) &&
				 validateConstraint3(strip, solution) &&
				 validateConstraint4(strip, solution)) {
				
				//add strip to solution
				Acquisition acq = new Acquisition();
				acq.setStrip_index(strip.getStrip_index());
				acq.setAcquisition_direction(strip.getStrip_direction());
				solution.addAcquisition(acq);
			}
			else
			{
				Acquisition acq = new Acquisition();
				acq.setStrip_index(strip.getStrip_index());
				acq.setAcquisition_direction(strip.getStrip_direction());
				acqNotAddedList.add(acq);
			}
		}
		return solution;
	}
	
	/**
	 * This method will update the solution by continuing to add any of strips, 
	 * which weren't added to the solution before, to it on condition that the updated solution
	 * must respect also the 4 constraints 
	 * @param solution
	 * @return the updated solution
	 */
	public SolutionVO updateSolution(SolutionVO solution) {
		
		for (Acquisition acq : acqNotAddedList) { //loop1
			//add acq before every acquisition in the solution
			for (int i = 0; i <= solution.getNumberOfAcquisitions(); i++) { //loop2
				//acq.setAcquisition_direction((int) (Math.random() * 100) % 2);
				solution.addAcquisition(i, acq);
				if (ValidatorSolution.verifySolution(instance, solution))
					break; //exit the loop2 if acq respects the 4 constraints.. (& continue the loop1)
				solution.removeAcquisition(i);
			}
		}
		
		return solution;
	}

	/**
	 * Used to validate the strip we want to add to the solution
	 * <p>
	 * The method then call the method {@code validateConstraint1} on the strip
	 * @param strip the strip that we want to add to the solution
	 * @return true if the strip respect the constraint 1
	 */
	private boolean validateConstraint1(Strip strip) {		
		return strip.validateConstraint1();		
	}
	
	/**
	 * Used to validate the strip we want to add to the solution
	 * <p>
	 * The method then call the method {@code validateConstraint2} on the strip
	 * @param strip the strip that we want to add to the solution
	 * @param solution - the solution
	 * @return true if the strip respect the constraint 2
	 */
	private boolean validateConstraint2(Strip strip, SolutionVO solution) {
		List<Strip> stripListOfSolution = instance.getStripListFrom(solution);
		Strip theLastStripOfSolution = stripListOfSolution.get(stripListOfSolution.size() - 1);
		
		return strip.validateConstraint2(theLastStripOfSolution);
	}
	
	/**
	 * Used to validate the strip we want to add to the solution
	 * <p>
	 * The method then call the method {@code validateConstraint3} on the strip
	 * @param strip - the strip that we want to add to the solution
	 * @param solution - the solution
	 * @return true if the strip respect the constraint 3
	 */
	private boolean validateConstraint3(Strip strip, SolutionVO solution) {
		List<Strip> stripList = instance.getStripListFrom(solution);
		
		return strip.validateConstraint3(stripList);
	}
	
	/**
	 * Used to validate the strip we want to add to the solution
	 * <p>
	 * The method then call the method {@code validateConstraint4} on the strip
	 * @param strip - the strip that we want to add to the solution
	 * @param solution - the solution
	 * @return true if the strip respect the constraint 4
	 */
	private boolean validateConstraint4(Strip strip, SolutionVO solution) {
		List<Strip> stripList = instance.getStripListFrom(solution);
		
		return strip.validateConstraint4(stripList);
	}
}
